#define SM_EPSLON 0.01
#define kernel_size 5

struct INPUT_SHADOW
{
	float3 pos;
};

struct AmountLight
{
	float amountLight_scene;
	float amountLight_hair;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////
// Generate shadow map
///////////////////////////////////////////////////////////////////////////////////////////////////////
[earlydepthstencil]
float4 PS_GenerateSM( VS_OUTPUT_SM input ):SV_Target
{
	float4 projPos = mul( float4(input.texPos, 1), g_mViewProjLight );
    float depth = projPos.z/projPos.w;

    return float4(depth, depth, depth, 1);    
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
// render with shadow effects
////////////////////////////////////////////////////////////////////////////////////////////////////////
// normal shadow map (with build-in filtering:"SampleCmp")
AmountLight ComputeShadow_SM(INPUT_SHADOW input)
{
	float4 projPosLight = mul(float4(input.pos,1), g_mViewProjLight);
    float2 texSM = float2(projPosLight.x/projPosLight.w+1, -projPosLight.y/projPosLight.w+1)*0.5;
	float depth = projPosLight.z/projPosLight.w;
	float epslon = depth * SM_EPSLON;

	AmountLight amountLight;
	amountLight.amountLight_scene =  g_txSMScene.SampleCmpLevelZero(g_samShadow, texSM, depth-epslon);
	
	return amountLight;
}

// PCF shadow map
AmountLight ComputeShadow_PCFSM(INPUT_SHADOW input)
{
	float4 projPosLight = mul(float4(input.pos,1), g_mViewProjLight);
    float2 texSM = float2(projPosLight.x/projPosLight.w+1, -projPosLight.y/projPosLight.w+1)*0.5;
	float depth = projPosLight.z/projPosLight.w;
	float epslon = depth * SM_EPSLON;	
	
	float shadow_scene = 0;
	float shadow_hair = 0;
	float n = 0;
	[unroll]for (int dx = (1-kernel_size)/2; dx <= kernel_size/2; dx++) 
	{ 
		[unroll]for (int dy = (1-kernel_size)/2; dy <= kernel_size/2; dy++) 
		{ 
			float size = 2.4;
			float sigma = (kernel_size/2.0)/size; // standard deviation, when kernel/2 > 3*sigma, it's close to zero, here we use 1.5 instead
			float exp = -1* (dx*dx + dy*dy)/ (2* sigma * sigma);
			float weight = 1/(2*PI*sigma*sigma) * pow(e, exp);

			shadow_scene += weight * g_txSMScene.SampleCmpLevelZero(g_samShadow, texSM, depth-epslon, int2(dx, dy));
			n+= weight;
		}
	}

	AmountLight amountLight;
	amountLight.amountLight_scene = shadow_scene/n;
	return amountLight;
}

AmountLight ComputeShadow(INPUT_SHADOW input, int iTechSM)
{
	AmountLight amountLight;
	amountLight.amountLight_hair = 1;
	amountLight.amountLight_scene = 1;

	switch(iTechSM)
	{
	case SHADOW_SM:
		amountLight = ComputeShadow_SM(input);
		break;
	case SHADOW_PCFSM:
		amountLight = ComputeShadow_PCFSM(input);
		break;
	}
	return amountLight;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Display shadow map on screen quad
//////////////////////////////////////////////////////////////////////////////////////////////////////////
float Display_SM(float2 tex)
{
	float depthScene = g_txSMScene.SampleLevel(g_samLinearClamp, tex, 0);
	return depthScene;
}

float4 PS_SQDrawSM(VS_OUTPUT_SCREENQUAD input):SV_TARGET
{
	float2 tex = input.vTex;
	float value = 1;
	
	switch(g_iTechSM)
	{
	case SHADOW_SM:
		value = Display_SM(tex);
		break;
	case SHADOW_PCFSM:
		value = Display_SM(tex);
		break;
	case SHADOW_SDSM:
		break;
	case SHADOW_UFGVSM:
		break;
	case SHADOW_KBVSM:
		break;
	}

	float4 color = float4(value, value, value, 1);
	return color;
}
